package club.jdiy.core.base;

import club.jdiy.core.base.domain.JpaEntity;
import club.jdiy.core.base.domain.Pager;
import club.jdiy.utils.StringUtils;
import com.querydsl.core.BooleanBuilder;
import lombok.Getter;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.util.List;
/**
 * JPA query 动态查询条件和默认排序字段.
 *
 * 由实现了{@link club.jdiy.core.base.domain.JpaFilterable} 的实体类中
 * {@link club.jdiy.core.base.domain.JpaFilterable#createFilter(WhereBuilder)}方法构造查询条件返回。
 *
 * @author ziquee(子秋) QQ号:39886616 主页: http://www.jdiy.club
 * @version 20210708
 */
@Getter
public final class JpaFilter {
    private final Class<? extends JpaEntity<?>> domainClass;
    private final String defaultOrderBy;
    private final WhereBuilder builder;
    private String cql;
    private String sql;

    public JpaFilter(Class<? extends JpaEntity<?>> thisEntityClass, WhereBuilder builder, String defaultOrderBy) {
        this(thisEntityClass, builder, defaultOrderBy, "");
    }

    public JpaFilter(Class<? extends JpaEntity<?>> thisEntityClass, WhereBuilder builder, String defaultOrderBy, String joins) {
        this.domainClass = thisEntityClass;
        if (joins == null) joins = "";
        cql = String.format("SELECT COUNT(o) FROM %s o %s", thisEntityClass.getName(), joins);
        sql = String.format("SELECT o FROM %s o %s", thisEntityClass.getName(), joins);
        String where = builder.toString();
        if (!"".equals(where = where.trim()) && !"1=1".equals(where)) {
            if (where.startsWith("1=1 AND ") || where.startsWith("1=1 and ")) {
                where = where.substring(8);
            }
            cql += " WHERE " + where;
            sql += " WHERE " + where;
        }
        this.defaultOrderBy = defaultOrderBy;
        this.builder = builder;
    }

    final long getCount(EntityManager em) {
        return ((Long) createQuery(em, this.cql).getSingleResult());
    }

    final <T> Pager<T> getPager(EntityManager em, int pageSize, int page, String order, String sort) {
        long rowCount = -1;
        Query query = createQuery(em, this.sql + getOrderBy(order, sort));
        if (pageSize > 0) {
            try {
                rowCount = getCount(em);
            } catch (NoResultException ignore) {
                rowCount = 0;
            }
            if (page < 1) page = 1;
            query.setFirstResult(pageSize * (page - 1));
            query.setMaxResults(pageSize);
        }
        @SuppressWarnings("unchecked")
        List<T> items = query.getResultList();
        if (rowCount == -1) rowCount = items.size();
        return new Pager<>(page, pageSize, rowCount, items);
    }

    @SuppressWarnings("ALL")
    final <T> List<T> getList(EntityManager em, int size, String order, String sort) {
        Query query = createQuery(em, this.sql + getOrderBy(order, sort));
        if (size > 0) query.setMaxResults(size);
        return query.getResultList();
    }

    private Query createQuery(EntityManager em, String q) {
        Query query = em.createQuery(q);
        builder.getParams().forEach(query::setParameter);
        return query;
    }

    private String getOrderBy(String order, String sort) {
        if (StringUtils.hasText(order)) return " ORDER BY o." + order + " " + sort;
        else if (StringUtils.hasText(defaultOrderBy)) return " ORDER BY " + defaultOrderBy;
        else return "";
    }
}